﻿namespace SuperMarieApi.Log.LogAgent.LogAgent
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Text;

    using SuperMarieApi.Log.LogAgent.Xml;

    internal abstract class BaseLog : ILog
    {
        #region 构造函数
        protected object log;
        protected LogInfo logInfo = new LogInfo();

        /// <summary>
        /// 构造函数
        /// </summary>
        public BaseLog()
        {
            #region 初始化 LogInfo
            this.Enable = false;
            var adapterConfigFile = this.getConfigFile("LogAgent.config");
            if (adapterConfigFile.Exists)
            {
                XReader reader = new XReader(adapterConfigFile.FullName, typeof(LogInfo).Name);
                var infos = new XHelper().TMapper<LogInfo>(reader);
                if (infos != null && infos.Length > 0)
                {
                    this.logInfo = infos[0];
                }
                this.logInfo.AdapterConfigFile = adapterConfigFile;
            }
            else
            {
                this.DefaultLogConfig();
            }
            #endregion
            #region 获取 LogAssembly
            this.getLogAssembly();
            if (this.logInfo.LogAssembly == null)
            {
                return;
            }
            #endregion
            #region 获取 LogConfigFile
            this.getLogConfig();
            #endregion
            #region 初始化Log
            this.IniLogAssembly();
            this.FrameIndex = this.logInfo.FrameIndex;
            this.IsLogStackTrace = this.logInfo.IsLogStackTrace;
            this.Enable = true;
            #endregion
        }
        #endregion
        #region 纯虚函数
        /// <summary>
        /// 默认日志配置
        /// </summary>
        protected abstract void DefaultLogConfig();
        /// <summary>
        /// 初始化日志程序集函数
        /// </summary>
        protected abstract void IniLogAssembly();
        #endregion

        #region 文件路径
        /// <summary>
        /// 获取当前Assembly文件
        /// </summary>
        /// <returns></returns>
        private FileInfo getAssemblyFile()
        {
            string assFile = new System.Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
            return new FileInfo(assFile);
        }

        /// <summary>
        /// 获取配置文件路径 - 当前文件夹
        /// </summary>
        /// <returns></returns>
        private FileInfo getCurrentFilePath(DirectoryInfo currentDir, string filePath, string fileName)
        {
            string path = string.Empty;
            path = currentDir.FullName;
            if (!path.EndsWith("\\"))
            {
                path = path + "\\";
            }
            if ((filePath != null) && (!string.IsNullOrEmpty(filePath)))
            {
                path = path + filePath + "\\";
            }
            path = path + fileName;
            return new FileInfo(path);
        }

        /// <summary>
        /// 获取配置文件路径 - 父级文件夹
        /// </summary>
        /// <returns></returns>
        private FileInfo getParentFilePath(DirectoryInfo currentDir, string filePath, string fileName)
        {
            return this.getCurrentFilePath(currentDir.Parent, filePath, fileName);
        }

        /// <summary>
        /// 获取配置文件路径 - 当前和父级文件夹
        /// </summary>
        /// <returns></returns>
        private FileInfo getFilePath(DirectoryInfo currentDir, string filePath, string fileName)
        {
            FileInfo result = this.getCurrentFilePath(currentDir, filePath, fileName);
            if (!result.Exists)
            {
                result = this.getParentFilePath(currentDir, filePath, fileName);
            }
            return result;
        }
        /// <summary>
        /// 获取配置文件路径 - 当前文件夹
        /// </summary>
        /// <returns></returns>
        private FileInfo getCurrentAssemblyFile(string configPath, string configName)
        {
            var currentDir = this.getAssemblyFile().Directory;
            return this.getFilePath(currentDir, configPath, configName);
        }
        /// <summary>
        /// 获取配置文件路径
        /// </summary>
        /// <param name="configName"></param>
        /// <returns></returns>
        private FileInfo getConfigFile(string configName)
        {
            FileInfo result = this.getCurrentAssemblyFile(string.Empty, configName);
            if (!result.Exists)
            {
                result = this.getCurrentAssemblyFile("LogAgent", configName);
            }
            if (!result.Exists)
            {
                result = this.getCurrentAssemblyFile("config", configName);
            }
            return result;
        }
        /// <summary>
        /// 获取配置文件路径 - 当前文件夹
        /// </summary>
        /// <returns></returns>
        private FileInfo getLogFile(string configPath, string configName)
        {
            var currentDir = this.getAssemblyFile().Directory;
            var configfi = this.logInfo.AdapterConfigFile;
            if (configfi != null && configfi.Exists)
            {
                currentDir = configfi.Directory;
            }
            return this.getFilePath(currentDir, configPath, configName);
        }
        /// <summary>
        /// 获取配置文件路径
        /// </summary>
        /// <param name="configName"></param>
        /// <returns></returns>
        private FileInfo getLogFile(string configName)
        {
            FileInfo result = this.getLogFile(string.Empty, configName);
            if (!result.Exists)
            {
                result = this.getLogFile("LogAgent", configName);
            }
            if (!result.Exists)
            {
                result = this.getLogFile("config", configName);
            }
            return result;
        }
        #endregion

        #region 初始化Log
        private void getLogAssembly()
        {
            var fileName = this.logInfo.LogFileName;
            if (!fileName.Contains(":"))
            {
                fileName = this.getLogFile(fileName).FullName;
            }
            var fi = new FileInfo(fileName);
            if (fi.Exists)
            {
                this.logInfo.LogAssembly = Assembly.UnsafeLoadFrom(fi.FullName);
            }
        }
        private void getLogConfig()
        {
            if (string.IsNullOrEmpty(this.logInfo.LogConfigName))
            {
                this.logInfo.LogConfigFile = this.logInfo.AdapterConfigFile;
            }
            else
            {
                this.logInfo.LogConfigFile = this.getLogFile(this.logInfo.LogConfigName);
                if ((this.logInfo.LogConfigFile == null) || (!this.logInfo.LogConfigFile.Exists))
                {
                    string assFile = new System.Uri(this.logInfo.LogAssembly.CodeBase).LocalPath;
                    assFile = Path.Combine(new FileInfo(assFile).Directory.FullName, this.logInfo.LogConfigName);
                    this.logInfo.LogConfigFile = new FileInfo(assFile);
                }
            }
        }
        #endregion

        #region 获取日志函数信息
        private string getLogClassMethod()
        {
            var frameIndex = this.FrameIndex + 4;
            var result = new StringBuilder();
            var st = new StackTrace(true);
            var sf = st.GetFrame(frameIndex);
            var sm = sf.GetMethod();
            FileInfo fi = new FileInfo(sm.Module.FullyQualifiedName);
            if (fi.Exists)
            {
                result.AppendLine("程序集：" + fi.Name);
            }
            try
            {
                result.AppendLine("函数：" + sm.DeclaringType.FullName + "." + sm.Name);
            }
            catch { }
            try
            {
                result.AppendLine("文件：" + sf.GetFileName());
            }
            catch { }
            try
            {
                int iFileLineNumber = sf.GetFileLineNumber();
                if (iFileLineNumber > 0)
                {
                    result.AppendLine("行：" + iFileLineNumber.ToString());
                }
            }
            catch { }
            return result.ToString().Trim();
        }
        private void thisDebugLogClassMethod()
        {
            if (this.IsLogStackTrace)
            {
                var ss = this.getLogClassMethod();
                if (!string.IsNullOrEmpty(ss))
                {
                    this.runDebugMethod(ss);
                }
            }
        }

        private void thisPrintLogClassMethod()
        {
            if (this.IsLogStackTrace)
            {
                var ss = this.getLogClassMethod();
                if (!string.IsNullOrEmpty(ss))
                {
                    this.runDebugMethod(ss);
                }
            }
        }
        public void DebugLogClassMethod()
        {
            this.thisDebugLogClassMethod();
        }
        #endregion

        #region 日志扩展
        /// <summary>
        /// 是否进行日志代码标识
        /// </summary>
        public bool IsLogStackTrace { get; set; }
        /// <summary>
        /// 主函数层级
        /// </summary>
        public int FrameIndex { get; set; }
        #endregion

        #region 反射调用日志类
        #region 反射调用日志方法
        private string getLogMethodName()
        {
            var frameIndex = 1;
            var result = string.Empty;
            var st = new StackTrace(true);
            var sf = st.GetFrame(frameIndex);
            var sm = sf.GetMethod();
            try
            {
                result = sm.Name;
            }
            catch { }
            return result.ToString().Trim();
        }

        private void InvokeLogMethod(string methodName, params object[] ps)
        {
            var types = new List<Type>();
            for (int i = 0; i < ps.Length; i++)
            {
                types.Add((ps[i]).GetType());
            }
            var method = this.log.GetType().GetMethod(methodName, types.ToArray());
            method.Invoke(this.log, ps);
        }


        private void runLogMethod(string methodName, params object[] ps)
        {
            if (this.log == null)
            {
                return;
            }
            this.thisDebugLogClassMethod();
            this.InvokeLogMethod(methodName, ps);
        }

        private void runLogMethod(params object[] ps)
        {
            if (this.log == null)
            {
                return;
            }
            this.thisDebugLogClassMethod();
            this.InvokeLogMethod("Debug", ps);
        }

        private void runPrintLogMethod(params object[] ps)
        {
            if (this.log == null)
            {
                return;
            }
            this.InvokeLogMethod("Debug", ps);
        }

        private void runDebugMethod(string message)
        {
            this.InvokeLogMethod("Debug", message);
        }
        #endregion

        #region 反射获取日志属性
        /// <summary>
        /// 反射获取属性值
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private bool getPropertyValue(string propertyName)
        {
            if (this.log == null)
            {
                return false;
            }
            var p = this.log.GetType().GetProperty(propertyName);
            return (bool)p.GetValue(this.log, null);
        }
        #endregion
        #endregion

        #region ILog
        #region ILog  属性
        public bool Enable { get; private set; }
        public bool IsTraceEnabled
        {
            get { return this.getPropertyValue("IsDebugEnabled"); }
        }
        public bool IsDebugEnabled
        {
            get { return this.getPropertyValue("IsDebugEnabled"); }
        }

        public bool IsInfoEnabled
        {
            get { return this.getPropertyValue("IsInfoEnabled"); }
        }

        public bool IsWarnEnabled
        {
            get { return this.getPropertyValue("IsWarnEnabled"); }
        }

        public bool IsErrorEnabled
        {
            get { return this.getPropertyValue("IsErrorEnabled"); }
        }

        public bool IsFatalEnabled
        {
            get { return this.getPropertyValue("IsFatalEnabled"); }
        }
        #endregion
        #region ILog  方法
        #region Trace
        public void Trace(object message)
        {
            this.runLogMethod(this.getLogMethodName(), message);
        }
        public void Trace(object message, Exception exception)
        {
            this.runLogMethod(this.getLogMethodName(), message, exception);
        }
        public void TraceFormat(string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), format, args);
        }
        public void TraceFormat(string format, object arg0)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0);
        }
        public void TraceFormat(string format, object arg0, object arg1)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1);
        }
        public void TraceFormat(string format, object arg0, object arg1, object arg2)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1, arg2);
        }
        public void TraceFormat(IFormatProvider provider, string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), provider, format, args);
        }
        #endregion
        #region Debug
        public void Debug(object message)
        {
            this.runLogMethod(this.getLogMethodName(), message);
        }
        public void Debug(object message, Exception exception)
        {
            this.runLogMethod(this.getLogMethodName(), message, exception);
        }
        public void DebugFormat(string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), format, args);
        }
        public void DebugFormat(string format, object arg0)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0);
        }
        public void DebugFormat(string format, object arg0, object arg1)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1);
        }
        public void DebugFormat(string format, object arg0, object arg1, object arg2)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1, arg2);
        }
        public void DebugFormat(IFormatProvider provider, string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), provider, format, args);
        }
        #endregion
        #region Info
        public void Info(object message)
        {
            this.runLogMethod(this.getLogMethodName(), message);
        }
        public void Info(object message, Exception exception)
        {
            this.runLogMethod(this.getLogMethodName(), message, exception);
        }
        public void InfoFormat(string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), format, args);
        }
        public void InfoFormat(string format, object arg0)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0);
        }
        public void InfoFormat(string format, object arg0, object arg1)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1);
        }
        public void InfoFormat(string format, object arg0, object arg1, object arg2)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1, arg2);
        }
        public void InfoFormat(IFormatProvider provider, string format, params object[] args)
        {

            this.runLogMethod(this.getLogMethodName(), provider, format, args);
        }
        #endregion
        #region Warn
        public void Warn(object message)
        {
            this.runLogMethod(this.getLogMethodName(), message);
        }
        public void Warn(object message, Exception exception)
        {
            this.runLogMethod(this.getLogMethodName(), message, exception);
        }
        public void WarnFormat(string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), format, args);
        }
        public void WarnFormat(string format, object arg0)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0);
        }
        public void WarnFormat(string format, object arg0, object arg1)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1);
        }
        public void WarnFormat(string format, object arg0, object arg1, object arg2)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1, arg2);
        }
        public void WarnFormat(IFormatProvider provider, string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), provider, format, args);
        }
        #endregion
        #region Error
        public void Error(object message)
        {
            this.runLogMethod(this.getLogMethodName(), message);
        }
        public void Error(object message, Exception exception)
        {
            this.runLogMethod(this.getLogMethodName(), message, exception);
        }
        public void ErrorFormat(string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), format, args);
        }
        public void ErrorFormat(string format, object arg0)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0);
        }
        public void ErrorFormat(string format, object arg0, object arg1)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1);
        }
        public void ErrorFormat(string format, object arg0, object arg1, object arg2)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1, arg2);
        }
        public void ErrorFormat(IFormatProvider provider, string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), provider, format, args);
        }
        #endregion
        #region Fatal
        public void Fatal(object message)
        {

            this.runLogMethod(this.getLogMethodName(), message);
        }
        public void Fatal(object message, Exception exception)
        {
            this.runLogMethod(this.getLogMethodName(), message, exception);
        }
        public void FatalFormat(string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), format, args);
        }
        public void FatalFormat(string format, object arg0)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0);
        }
        public void FatalFormat(string format, object arg0, object arg1)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1);
        }
        public void FatalFormat(string format, object arg0, object arg1, object arg2)
        {
            this.runLogMethod(this.getLogMethodName(), format, arg0, arg1, arg2);
        }
        public void FatalFormat(IFormatProvider provider, string format, params object[] args)
        {
            this.runLogMethod(this.getLogMethodName(), provider, format, args);
        }
        #endregion
        #endregion
        #endregion

        public void Print(object message)
        {
            Console.WriteLine(message);
            this.runPrintLogMethod(message);
        }

    }
}
